package se.ytterman.jserial.framework.device;

import java.lang.*;
import java.io.*;
import java.util.*;

import java.util.regex.*;

import javax.xml.xpath.*;

import org.w3c.dom.*;

import se.ytterman.jserial.util.*;
import se.ytterman.jserial.framework.device.*;

public class ResponseFactory {
    
    public ResponseFactory(Document responseModelDocument) { 
        // Create new arraylist
        this.inputPatterns = new ArrayList();
        
        // Set reference to DOM tree
        this.responseModelDocument = responseModelDocument;
    }
    
    public void loadModel() {
        // Disassemble XML dom tree
        try {
            // Extract the events
            XPathFactory xpathFactory = XPathFactory.newInstance();
            XPath eventExtractor = xpathFactory.newXPath();
                    
            // Iterate over found events
            NodeList responseNodes = (NodeList)eventExtractor.evaluate("/Tokens/Responses/*", 
                                                                        this.responseModelDocument, 
                                                                        XPathConstants.NODESET);
 
            for(int i = 0; i < responseNodes.getLength(); i++) {
                // Extract event information
                Element responseNode = (Element)responseNodes.item(i);
                String responseId = responseNode.getAttribute("id");
                String responseName = responseNode.getAttribute("name");
                
                // DEBUG System.out.println("Response: (" + responseId + ") " + responseName);

                NodeList formatNodes = responseNode.getElementsByTagName("ResponsePattern");

                for(int j = 0; j < formatNodes.getLength(); j++) {
                    Element formatNode = (Element)formatNodes.item(j);
                             
                    Node formatText = formatNode.getFirstChild();
                    
                    // Fetch text child nodes 
                    String formatString = formatText.getNodeValue();
                
                    // DEBUG System.out.println("\tFormat: " + formatString);
                    
                    // Create inputformat object
                    ResponsePattern currentResponsePattern = new ResponsePattern(formatString, responseId);                 
                    
                    // Register matching pattern
                    this.inputPatterns.add(currentResponsePattern);
                }
            }
        }
        catch(Exception e)
        {
        }
    }
    
    public Response newResponse(ByteArrayOutputStream inputBuffer) {
       // Create return value
       Response responseObject = null; 
        
       // Fetch string to compare against
       String testPattern = inputBuffer.toString();   
       
       // Iterate over registered input patterns
       Iterator <ResponsePattern> patterns = this.inputPatterns.iterator();

       // Iterate over all responses in this set...
       while(patterns.hasNext()) {                          
            // Get present response to match against...
            ResponsePattern currentPattern = patterns.next();    

            // Fetch matcher for the response pattern
            Matcher patternMatcher = currentPattern.matchPattern(testPattern);              

            // Check if we do have a match...
            if(patternMatcher.find()) {
                // DEBUG
                if(false) {
                    System.out.println("Got pattern for response: " + currentPattern.getResponseId() +
                                       "\r\nPattern: >" + patternMatcher.group() + 
                                       "< \r\n@ " + patternMatcher.start() + ":" + patternMatcher.end() +
                                       "(0:" + inputBuffer.size() + ")");
                }

                // Create array list of Strings to construct values
                String[] parameterStrings = new String[patternMatcher.groupCount()];
                
                for(int i = 0; i < patternMatcher.groupCount(); i++) {
                    // Add string value to array list...
                    parameterStrings[i] = patternMatcher.group(i);
                }

                // Create response object
                responseObject = this.newResponse(currentPattern.getResponseId(),
                                                  parameterStrings);                 
            }
        }
        
        // Return value
        return responseObject;
    }
    
    private Response newResponse(String responseId, String... parameterValues) {
        // Create return value
        Response responseObject = null;
        
        // Disassemble XML dom tree
        try {
            // Create helper classes...
            XPathFactory xpathFactory = XPathFactory.newInstance();
            XPath responseExtractor = xpathFactory.newXPath();
                    
            // Iterate over found events
            String responseXpath = "/Tokens/Responses/Response[@id='" + responseId + "']";
            NodeList responseNodes = (NodeList)responseExtractor.evaluate(responseXpath, 
                                                                          this.responseModelDocument, 
                                                                          XPathConstants.NODESET);
 
            if(responseNodes.getLength() == 1) {
                // Extract event information
                Element responseNode = (Element)responseNodes.item(0);
                String responseName = responseNode.getAttribute("name");
                
                // DEBUG System.out.println("Response: (" + responseId + ") " + responseName);
                
                // Create new response object
                responseObject = new Response(responseId, responseName);
                
                // Extract parameter definitions from XML token definition
                XPath parameterExtractor = xpathFactory.newXPath();
                
                String parameterXpath = "/Tokens/Responses/Response[@id='" + responseId + "']" +
                                        "/Parameters/*";
                
                NodeList parameterNodes = (NodeList)parameterExtractor.evaluate(parameterXpath, 
                                                                                this.responseModelDocument, 
                                                                                XPathConstants.NODESET);
                
                // Check number of parameters
                if(parameterValues.length < parameterNodes.getLength()) {
                    throw new Exception("Parameter number mismatch");
                }
                
                // Iterate over potential parameter tags...
                for(int j = 0; j < parameterNodes.getLength(); j++) {
                    // Iterate over parameter tags
                    Element parameterNode = (Element)parameterNodes.item(j);
                    
                    // Extract attributes
                    String parameterType = parameterNode.getAttribute("type");
                    String parameterName = parameterNode.getAttribute("name");
                                        
                    // Create constructor parameters (String)
                    String parameterString = parameterValues[j];
                    Object[] constructorParameters = new Object[1];
                    constructorParameters[0] = parameterString;
                    
                    // Create parameter object instance
                    Object parameterObject = InstanceFactory.newInstance(parameterType, 
                                                                         constructorParameters, 
                                                                         false);
                    
                    // Add parameter to response object
                    responseObject.addParameter(parameterName, parameterObject);
                }
            }
            else {
                System.out.println("APA");
                // XXX Throw exception
            }
        }
        catch(Exception e)
        {
            System.out.println("BEPA:");
            e.printStackTrace(System.out);
        }
        
        return responseObject;
     }
    
    private Document responseModelDocument;
    private ArrayList <ResponsePattern> inputPatterns;
}
